home *** CD-ROM | disk | FTP | other *** search
/ Clickx 115 / Clickx 115.iso / software / tools / windows / tails-i386-0.16.iso / live / filesystem.squashfs / usr / share / pyshared / xdg / DesktopEntry.py < prev    next >
Encoding:
Python Source  |  2010-06-23  |  15.6 KB  |  398 lines

  1. """
  2. Complete implementation of the XDG Desktop Entry Specification Version 1.0
  3. http://standards.freedesktop.org/desktop-entry-spec/
  4.  
  5. Not supported:
  6. - Encoding: Legacy Mixed
  7. - Does not check exec parameters
  8. - Does not check URL's
  9. - Does not completly validate deprecated/kde items
  10. - Does not completly check categories
  11. """
  12.  
  13. from xdg.IniFile import *
  14. from xdg.BaseDirectory import *
  15. import os.path
  16.  
  17. class DesktopEntry(IniFile):
  18.     "Class to parse and validate DesktopEntries"
  19.  
  20.     defaultGroup = 'Desktop Entry'
  21.  
  22.     def __init__(self, filename=None):
  23.         self.content = dict()
  24.         if filename and os.path.exists(filename):
  25.             self.parse(filename)
  26.         elif filename:
  27.             self.new(filename)
  28.  
  29.     def __str__(self):
  30.         return self.getName()
  31.  
  32.     def parse(self, file):
  33.         IniFile.parse(self, file, ["Desktop Entry", "KDE Desktop Entry"])
  34.  
  35.     # start standard keys
  36.     def getType(self):
  37.         return self.get('Type')
  38.     """ @deprecated, use getVersionString instead """
  39.     def getVersion(self):
  40.         return self.get('Version', type="numeric")
  41.     def getVersionString(self):
  42.         return self.get('Version')
  43.     def getName(self):
  44.         return self.get('Name', locale=True)
  45.     def getGenericName(self):
  46.         return self.get('GenericName', locale=True)
  47.     def getNoDisplay(self):
  48.         return self.get('NoDisplay', type="boolean")
  49.     def getComment(self):
  50.         return self.get('Comment', locale=True)
  51.     def getIcon(self):
  52.         return self.get('Icon', locale=True)
  53.     def getHidden(self):
  54.         return self.get('Hidden', type="boolean")
  55.     def getOnlyShowIn(self):
  56.         return self.get('OnlyShowIn', list=True)
  57.     def getNotShowIn(self):
  58.         return self.get('NotShowIn', list=True)
  59.     def getTryExec(self):
  60.         return self.get('TryExec')
  61.     def getExec(self):
  62.         return self.get('Exec')
  63.     def getPath(self):
  64.         return self.get('Path')
  65.     def getTerminal(self):
  66.         return self.get('Terminal', type="boolean")
  67.     """ @deprecated, use getMimeTypes instead """
  68.     def getMimeType(self):
  69.         return self.get('MimeType', list=True, type="regex")
  70.     def getMimeTypes(self):
  71.         return self.get('MimeType', list=True)
  72.     def getCategories(self):
  73.         return self.get('Categories', list=True)
  74.     def getStartupNotify(self):
  75.         return self.get('StartupNotify', type="boolean")
  76.     def getStartupWMClass(self):
  77.         return self.get('StartupWMClass')
  78.     def getURL(self):
  79.         return self.get('URL')
  80.     # end standard keys
  81.  
  82.     # start kde keys
  83.     def getServiceTypes(self):
  84.         return self.get('ServiceTypes', list=True)
  85.     def getDocPath(self):
  86.         return self.get('DocPath')
  87.     def getKeywords(self):
  88.         return self.get('Keywords', list=True, locale=True)
  89.     def getInitialPreference(self):
  90.         return self.get('InitialPreference')
  91.     def getDev(self):
  92.         return self.get('Dev')
  93.     def getFSType(self):
  94.         return self.get('FSType')
  95.     def getMountPoint(self):
  96.         return self.get('MountPoint')
  97.     def getReadonly(self):
  98.         return self.get('ReadOnly', type="boolean")
  99.     def getUnmountIcon(self):
  100.         return self.get('UnmountIcon', locale=True)
  101.     # end kde keys
  102.  
  103.     # start deprecated keys
  104.     def getMiniIcon(self):
  105.         return self.get('MiniIcon', locale=True)
  106.     def getTerminalOptions(self):
  107.         return self.get('TerminalOptions')
  108.     def getDefaultApp(self):
  109.         return self.get('DefaultApp')
  110.     def getProtocols(self):
  111.         return self.get('Protocols', list=True)
  112.     def getExtensions(self):
  113.         return self.get('Extensions', list=True)
  114.     def getBinaryPattern(self):
  115.         return self.get('BinaryPattern')
  116.     def getMapNotify(self):
  117.         return self.get('MapNotify')
  118.     def getEncoding(self):
  119.         return self.get('Encoding')
  120.     def getSwallowTitle(self):
  121.         return self.get('SwallowTitle', locale=True)
  122.     def getSwallowExec(self):
  123.         return self.get('SwallowExec')
  124.     def getSortOrder(self): 
  125.         return self.get('SortOrder', list=True)
  126.     def getFilePattern(self):
  127.         return self.get('FilePattern', type="regex")
  128.     def getActions(self):
  129.         return self.get('Actions', list=True)
  130.     # end deprecated keys
  131.  
  132.     # desktop entry edit stuff
  133.     def new(self, filename):
  134.         if os.path.splitext(filename)[1] == ".desktop":
  135.             type = "Application"
  136.         elif os.path.splitext(filename)[1] == ".directory":
  137.             type = "Directory"
  138.         else:
  139.             raise ParsingError("Unknown extension", filename)
  140.  
  141.         self.content = dict()
  142.         self.addGroup(self.defaultGroup)
  143.         self.set("Type", type)
  144.         self.filename = filename
  145.     # end desktop entry edit stuff
  146.  
  147.     # validation stuff
  148.     def checkExtras(self):
  149.         # header
  150.         if self.defaultGroup == "KDE Desktop Entry":
  151.             self.warnings.append('[KDE Desktop Entry]-Header is deprecated')
  152.  
  153.         # file extension
  154.         if self.fileExtension == ".kdelnk":
  155.             self.warnings.append("File extension .kdelnk is deprecated")
  156.         elif self.fileExtension != ".desktop" and self.fileExtension != ".directory":
  157.             self.warnings.append('Unknown File extension')
  158.  
  159.         # Type
  160.         try:
  161.             self.type = self.content[self.defaultGroup]["Type"]
  162.         except KeyError:
  163.             self.errors.append("Key 'Type' is missing")
  164.  
  165.         # Name
  166.         try:
  167.             self.name = self.content[self.defaultGroup]["Name"]
  168.         except KeyError:
  169.             self.errors.append("Key 'Name' is missing")
  170.  
  171.     def checkGroup(self, group):
  172.         # check if group header is valid
  173.         if not (group == self.defaultGroup \
  174.         or re.match("^\Desktop Action [a-zA-Z]+\$", group) \
  175.         or (re.match("^\X-", group) and group.decode("utf-8", "ignore").encode("ascii", 'ignore') == group)):
  176.             self.errors.append("Invalid Group name: %s" % group)
  177.         else:
  178.             #OnlyShowIn and NotShowIn
  179.             if self.content[group].has_key("OnlyShowIn") and self.content[group].has_key("NotShowIn"):
  180.                 self.errors.append("Group may either have OnlyShowIn or NotShowIn, but not both")
  181.  
  182.     def checkKey(self, key, value, group):
  183.         # standard keys     
  184.         if key == "Type":
  185.             if value == "ServiceType" or value == "Service" or value == "FSDevice":
  186.                 self.warnings.append("Type=%s is a KDE extension" % key)
  187.             elif value == "MimeType":
  188.                 self.warnings.append("Type=MimeType is deprecated")
  189.             elif not (value == "Application" or value == "Link" or value == "Directory"):
  190.                 self.errors.append("Value of key 'Type' must be Application, Link or Directory, but is '%s'" % value)
  191.  
  192.             if self.fileExtension == ".directory" and not value == "Directory":
  193.                 self.warnings.append("File extension is .directory, but Type is '%s'" % value)
  194.             elif self.fileExtension == ".desktop" and value == "Directory":
  195.                 self.warnings.append("Files with Type=Directory should have the extension .directory")
  196.  
  197.             if value == "Application":
  198.                 if not self.content[group].has_key("Exec"):
  199.                     self.warnings.append("Type=Application needs 'Exec' key")
  200.             if value == "Link":
  201.                 if not self.content[group].has_key("URL"):
  202.                     self.warnings.append("Type=Application needs 'Exec' key")
  203.  
  204.         elif key == "Version":
  205.             self.checkValue(key, value)
  206.  
  207.         elif re.match("^Name"+xdg.Locale.regex+"$", key):
  208.             pass # locale string
  209.  
  210.         elif re.match("^GenericName"+xdg.Locale.regex+"$", key):
  211.             pass # locale string
  212.  
  213.         elif key == "NoDisplay":
  214.             self.checkValue(key, value, type="boolean")
  215.  
  216.         elif re.match("^Comment"+xdg.Locale.regex+"$", key):
  217.             pass # locale string
  218.  
  219.         elif re.match("^Icon"+xdg.Locale.regex+"$", key):
  220.             self.checkValue(key, value)
  221.  
  222.         elif key == "Hidden":
  223.             self.checkValue(key, value, type="boolean")
  224.  
  225.         elif key == "OnlyShowIn":
  226.             self.checkValue(key, value, list=True)
  227.             self.checkOnlyShowIn(value)
  228.  
  229.         elif key == "NotShowIn":
  230.             self.checkValue(key, value, list=True)
  231.             self.checkOnlyShowIn(value)
  232.  
  233.         elif key == "TryExec":
  234.             self.checkValue(key, value)
  235.             self.checkType(key, "Application")
  236.  
  237.         elif key == "Exec":
  238.             self.checkValue(key, value)
  239.             self.checkType(key, "Application")
  240.  
  241.         elif key == "Path":
  242.             self.checkValue(key, value)
  243.             self.checkType(key, "Application")
  244.  
  245.         elif key == "Terminal":
  246.             self.checkValue(key, value, type="boolean")
  247.             self.checkType(key, "Application")
  248.  
  249.         elif key == "MimeType":
  250.             self.checkValue(key, value, list=True)
  251.             self.checkType(key, "Application")
  252.  
  253.         elif key == "Categories":
  254.             self.checkValue(key, value)
  255.             self.checkType(key, "Application")
  256.             self.checkCategorie(value)
  257.  
  258.         elif key == "StartupNotify":
  259.             self.checkValue(key, value, type="boolean")
  260.             self.checkType(key, "Application")
  261.  
  262.         elif key == "StartupWMClass":
  263.             self.checkType(key, "Application")
  264.  
  265.         elif key == "URL":
  266.             self.checkValue(key, value)
  267.             self.checkType(key, "URL")
  268.  
  269.         # kde extensions
  270.         elif key == "ServiceTypes":
  271.             self.checkValue(key, value, list=True)
  272.             self.warnings.append("Key '%s' is a KDE extension" % key)
  273.  
  274.         elif key == "DocPath":
  275.             self.checkValue(key, value)
  276.             self.warnings.append("Key '%s' is a KDE extension" % key)
  277.  
  278.         elif re.match("^Keywords"+xdg.Locale.regex+"$", key):
  279.             self.checkValue(key, value, list=True)
  280.             self.warnings.append("Key '%s' is a KDE extension" % key)
  281.  
  282.         elif key == "InitialPreference":
  283.             self.checkValue(key, value, type="numeric")
  284.             self.warnings.append("Key '%s' is a KDE extension" % key)
  285.  
  286.         elif key == "Dev":
  287.             self.checkValue(key, value)
  288.             self.checkType(key, "FSDevice")
  289.             self.warnings.append("Key '%s' is a KDE extension" % key)
  290.  
  291.         elif key == "FSType":
  292.             self.checkValue(key, value)
  293.             self.checkType(key, "FSDevice")
  294.             self.warnings.append("Key '%s' is a KDE extension" % key)
  295.  
  296.         elif key == "MountPoint":
  297.             self.checkValue(key, value)
  298.             self.checkType(key, "FSDevice")
  299.             self.warnings.append("Key '%s' is a KDE extension" % key)
  300.  
  301.         elif key == "ReadOnly":
  302.             self.checkValue(key, value, type="boolean")
  303.             self.checkType(key, "FSDevice")
  304.             self.warnings.append("Key '%s' is a KDE extension" % key)
  305.  
  306.         elif re.match("^UnmountIcon"+xdg.Locale.regex+"$", key):
  307.             self.checkValue(key, value)
  308.             self.checkType(key, "FSDevice")
  309.             self.warnings.append("Key '%s' is a KDE extension" % key)
  310.  
  311.         # deprecated keys
  312.         elif key == "Encoding":
  313.             self.checkValue(key, value)
  314.             self.warnings.append("Key '%s' is deprecated" % key)
  315.  
  316.         elif re.match("^MiniIcon"+xdg.Locale.regex+"$", key):
  317.             self.checkValue(key, value)
  318.             self.warnings.append("Key '%s' is deprecated" % key)
  319.  
  320.         elif key == "TerminalOptions":
  321.             self.checkValue(key, value)
  322.             self.warnings.append("Key '%s' is deprecated" % key)
  323.  
  324.         elif key == "DefaultApp":
  325.             self.checkValue(key, value)
  326.             self.warnings.append("Key '%s' is deprecated" % key)
  327.  
  328.         elif key == "Protocols":
  329.             self.checkValue(key, value, list=True)
  330.             self.warnings.append("Key '%s' is deprecated" % key)
  331.  
  332.         elif key == "Extensions":
  333.             self.checkValue(key, value, list=True)
  334.             self.warnings.append("Key '%s' is deprecated" % key)
  335.  
  336.         elif key == "BinaryPattern":
  337.             self.checkValue(key, value)
  338.             self.warnings.append("Key '%s' is deprecated" % key)
  339.  
  340.         elif key == "MapNotify":
  341.             self.checkValue(key, value)
  342.             self.warnings.append("Key '%s' is deprecated" % key)
  343.  
  344.         elif re.match("^SwallowTitle"+xdg.Locale.regex+"$", key):
  345.             self.warnings.append("Key '%s' is deprecated" % key)
  346.  
  347.         elif key == "SwallowExec":
  348.             self.checkValue(key, value)
  349.             self.warnings.append("Key '%s' is deprecated" % key)
  350.  
  351.         elif key == "FilePattern":
  352.             self.checkValue(key, value, type="regex", list=True)
  353.             self.warnings.append("Key '%s' is deprecated" % key)
  354.  
  355.         elif key == "SortOrder":
  356.             self.checkValue(key, value, list=True)
  357.             self.warnings.append("Key '%s' is deprecated" % key)
  358.  
  359.         elif key == "Actions":
  360.             self.checkValue(key, value, list=True)
  361.             self.warnings.append("Key '%s' is deprecated" % key)
  362.  
  363.         # "X-" extensions
  364.         elif re.match("^X-[a-zA-Z0-9-]+", key):
  365.             pass
  366.  
  367.         else:
  368.             self.errors.append("Invalid key: %s" % key)
  369.  
  370.     def checkType(self, key, type):
  371.         if not self.getType() == type:
  372.             self.errors.append("Key '%s' only allowed in Type=%s" % (key, type))
  373.  
  374.     def checkOnlyShowIn(self, value):
  375.         values = self.getList(value)
  376.         valid = ["GNOME", "KDE", "ROX", "XFCE", "Old", "LXDE"]
  377.         for item in values:
  378.             if item not in valid and item[0:2] != "X-":
  379.                 self.errors.append("'%s' is not a registered OnlyShowIn value" % item);
  380.  
  381.     def checkCategorie(self, value):
  382.         values = self.getList(value)
  383.  
  384.         main = ["AudioVideo", "Audio", "Video", "Development", "Education", "Game", "Graphics", "Network", "Office", "Settings", "System", "Utility"]
  385.         hasmain = False
  386.         for item in values:
  387.             if item in main:
  388.                 hasmain = True
  389.         if hasmain == False:
  390.             self.errors.append("Missing main category")
  391.  
  392.         additional = ["Building", "Debugger", "IDE", "GUIDesigner", "Profiling", "RevisionControl", "Translation", "Calendar", "ContactManagement", "Database", "Dictionary", "Chart", "Email", "Finance", "FlowChart", "PDA", "ProjectManagement", "Presentation", "Spreadsheet", "WordProcessor", "2DGraphics", "VectorGraphics", "3DGraphics", "RasterGraphics", "Scanning", "OCR", "Photography", "Publishing", "Viewer", "TextTools", "DesktopSettings", "HardwareSettings", "Printing", "PackageManager", "Dialup", "InstantMessaging", "Chat", "IRCClient", "FileTransfer", "HamRadio", "News", "P2P", "RemoteAccess", "Telephony", "TelephonyTools", "VideoConference", "WebBrowser", "WebDevelopment", "Midi", "Mixer", "Sequencer", "Tuner", "TV", "AudioVideoEditing", "Player", "Recorder", "DiscBurning", "ActionGame", "AdventureGame", "ArcadeGame", "BoardGame", "BlocksGame", "CardGame", "KidsGame", "LogicGame", "RolePlaying", "Simulation", "SportsGame", "StrategyGame", "Art", "Construction", "Music", "Languages", "Science", "ArtificialIntelligence", "Astronomy", "Biology", "Chemistry", "ComputerScience", "DataVisualization", "Economy", "Electricity", "Geography", "Geology", "Geoscience", "History", "ImageProcessing", "Literature", "Math", "NumericalAnalysis", "MedicalSoftware", "Physics", "Robotics", "Sports", "ParallelComputing", "Amusement", "Archiving", "Compression", "Electronics", "Emulator", "Engineering", "FileTools", "FileManager", "TerminalEmulator", "Filesystem", "Monitor", "Security", "Accessibility", "Calculator", "Clock", "TextEditor", "Documentation", "Core", "KDE", "GNOME", "GTK", "Qt", "Motif", "Java", "ConsoleOnly", "Screensaver", "TrayIcon", "Applet", "Shell"]
  393.         
  394.         for item in values:
  395.             if item not in additional + main and item[0:2] != "X-":
  396.                 self.errors.append("'%s' is not a registered Category" % item);
  397.  
  398.